package com.fanqj.sentinel.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
import com.alibaba.csp.sentinel.util.TimeUtil;
import org.apache.tomcat.jni.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * 熔断降级实例
 * @author <a href="mailto:fanqijin@wxchina.com">fanqj</a>
 * @Date 2021年02月02日 14:01
 * @Version 6.4.1
 */
@RestController
@RequestMapping("/degrade")
public class DegradeController {

    final Logger logger = LoggerFactory.getLogger(FlowController.class);

    public static final String KEY = "degradeRTKey";
    public static final String KEY_RATIO = "degradeRatioKey";
    public static final String KEY_COUNT = "degradeCountKey";

    @GetMapping("/rt")
    public String returnTimeDegrade() {
        Entry entry = null;
        try {
            entry = SphU.entry(KEY);
            int anInt = ThreadLocalRandom.current().nextInt(40, 60);
            TimeUnit.MILLISECONDS.sleep(anInt);
            return "Success pass degrade!";
        } catch (Throwable t) {
            if (!BlockException.isBlockException(t)) {
                Tracer.trace(t);
            }
            return "Degrade by slow request";
        } finally {
            if (entry != null) {
                entry.exit();
            }
        }
    }

    @GetMapping("/ratio")
    @SentinelResource(value = KEY_RATIO, blockHandler = "exceptionRatioDegradeBlockHandler", fallback = "exceptionRatioDegradeFallback")
    public String exceptionRatioDegrade() {
        if (ThreadLocalRandom.current().nextInt(0, 100) > 55) {
            throw new RuntimeException("oops");
        }
        return "Success pass Degrade!";
    }

    public String exceptionRatioDegradeBlockHandler(BlockException blockException) {
        return "Degrade by slow request";
    }

    public String exceptionRatioDegradeFallback(Throwable t) {
        return "fall back request";
    }

    @GetMapping("/count")
    @SentinelResource(value = KEY_COUNT, fallback = "exceptionCountDegradeFallBack")
    public String exceptionCountDegrade() {
        if (ThreadLocalRandom.current().nextInt(0, 100) > 55) {
            throw new RuntimeException("oops");
        }
        return "Pass by exception count degrade!";
    }

    public String exceptionCountDegradeFallBack(Throwable throwable) {
        return "FallBack by exceptionCountDegrade!";
    }

    @PostConstruct
    public void initFlowRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rtRule = new DegradeRule(KEY).setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())
                //慢调用比例模式下为慢调用临界 RT（超出该值计为慢调用）；异常比例/异常数模式下为对应的阈值
                .setCount(50)
                //熔断触发的最小请求数
                .setMinRequestAmount(10)
                //慢调用比例阈值，仅慢调用比例模式有效
                .setSlowRatioThreshold(0.4d)
                //统计时长
                .setStatIntervalMs(2000)
                //熔断时长，单位为 s
                .setTimeWindow(10);
        rules.add(rtRule);
        DegradeRule ratioRule = new DegradeRule(KEY_RATIO).setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType())
                //慢调用比例模式下为慢调用临界 RT（超出该值计为慢调用）；异常比例/异常数模式下为对应的阈值
                .setCount(0.5)
                //熔断触发的最小请求数
                .setMinRequestAmount(50)
                //统计时长
                .setStatIntervalMs(1000)
                //熔断时长，单位为 s
                .setTimeWindow(10);
        rules.add(ratioRule);
        DegradeRuleManager.loadRules(rules);
        //状态切换事件监听
        EventObserverRegistry.getInstance().addStateChangeObserver("logging",
                (prevState, newState, rule, snapshotValue) -> {
                    if (newState == CircuitBreaker.State.OPEN) {
                        logger.warn("{} -> OPEN at {}, snapshotValue={}", prevState.name(),
                                TimeUtil.currentTimeMillis(), snapshotValue);
                    } else {
                        logger.warn("{} -> {} at {}", prevState.name(), newState.name(),
                                TimeUtil.currentTimeMillis());
                    }
                });
    }

}
